package com.cgs.query.monitor.metrics

import com.cgs.query.executor.ExecuteArgs
import com.cgs.query.util.Utils
import io.micrometer.core.instrument.Timer
import mu.KotlinLogging
import org.aspectj.lang.ProceedingJoinPoint
import org.aspectj.lang.annotation.Around
import org.aspectj.lang.annotation.Aspect
import org.aspectj.lang.annotation.Pointcut

private val logger = KotlinLogging.logger { }

@Aspect
open class MetricsExecuteAspect {

    @Pointcut("execution(* com.cgs.query.server.service.ServerQueryService.execute(..))")
    fun executePointCut() {
    }

    init {
        logger.info { "将对远程调用进行度量捕捉" }
    }

    @Around("executePointCut()")
    @Throws(Throwable::class)
    fun doAround(pjp: ProceedingJoinPoint): Any? {
        val executeArgs = ExecuteArgs(pjp.args)
        val timer = createTimer(executeArgs)
        val throwable = ThrowableHolder()
        val result = timer.recordCallable {
            try {
                return@recordCallable pjp.proceed()
            } catch (e: Throwable) {
                throwable.throwable = e
                (timer as? QueryMeterRegistry.TimerWrapper)?.fCount?.incrementAndGet()
                null
            }
        }
        Utils.notNullAndThen(throwable.throwable) {
            throw it
        }
        return result.apply {
            serverMark()
            projectMark(executeArgs)
        }
    }

    private fun createTimer(executeArgs: ExecuteArgs): Timer {
        return Timer.builder("server")
                .tag("project", executeArgs.projectName)
                .tag("callName", executeArgs.name)
                .register(MetricsHelper.INSTANCE.registry)
    }

    private fun serverMark() {
        CodahaleMetricsHelper.INSTANCE.registry.meter("server").mark()
    }

    private fun projectMark(executeArgs: ExecuteArgs) {
        CodahaleMetricsHelper.INSTANCE.registry.meter("server.${executeArgs.projectName}").mark()
    }
}

class ThrowableHolder {
    var throwable: Throwable? = null
}